package com.example.jsd2205.vx.emos.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.example.jsd2205.vx.emos.config.SystemConstants;
import com.example.jsd2205.vx.emos.db.dao.*;
import com.example.jsd2205.vx.emos.db.pojo.TbCheckin;
import com.example.jsd2205.vx.emos.db.pojo.TbFaceModel;
import com.example.jsd2205.vx.emos.exception.EmosException;
import com.example.jsd2205.vx.emos.service.CheckinService;
import com.example.jsd2205.vx.emos.task.EmailTask;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;

/** 业务层
 * 引用封装常量对象和持久层接口中的抽象方法
 * 实现员工考勤签到
 * 签到业务
 * 创建新员工人脸模型
 * @author admin闫
 * @author admin苗
 *
 */
@Service
@Scope("prototype")//后续有发送邮件的功能，所有设置成多例对象
@Slf4j
public class CheckinServiceImpl implements CheckinService {

    /**
     * 引用封装常量对象
     */
    @Autowired
    private SystemConstants constants;

    /**
     * 持久层的引用
     * 调用查询的方法
     * 判断是否为工作日、休息日、是否可以签到
     */
    @Autowired
    private TbHolidaysDao holidaysDao;
    @Autowired
    private TbWorkdayDao workdayDao;
    @Autowired
    private TbCheckinDao checkinDao;

    @Autowired
    private TbFaceModelDao faceModelDao;

    @Autowired
    private TbCityDao cityDao;

    @Autowired
    private TbUserDao userDao;

    @Value("${emos.face.createFaceModelUrl}")
    private String createFaceModelUrl;

    @Value("${emos.face.checkinUrl}")
    private String checkinUrl;

    @Value("${emos.email.hr}")
    private String hrEmail;

    @Value("${emos.code}")
    private String code;

    @Autowired
    private EmailTask emailTask;

    /**
     * 判断当前日期是节假日还是工作日
     * 实现员工考勤
     * @param userId 用户id
     * @param date 日期
     * @return boolean值
     */
    @Override
    public String validCanCheckIn(int userId, String date) {
        //判断是否为特殊节假日
        boolean bool_1 = holidaysDao.searchTodayIsHolidays() != null ? true : false;
        //判断是否为特殊工作日
        boolean bool_2 = workdayDao.searchTodayIsWorkday() != null ? true : false;
        //声明变量
        String type = "工作日";
        //通过DateUtil创建日期对象
        //调用date()方法
        //判断是否为周末
        if (DateUtil.date().isWeekend()) {
            type = "节假日";
        }
        //如果bool_1为true，修改type为节假日
        // bool_1、bool_2不能同时为true，不符合逻辑(不能即为节假日，又为工作日)
        if (bool_1) {
            type = "节假日";
        //如果bool_2为true，修改type为工作日
        } else if (bool_2) {
            type = "工作日";
        }
        //如果当天为节假日，则不需要考勤，然后返回给客户端
        if (type.equals("节假日")) {
            return "节假日不需要考勤";
        } else {
            //获取日期对象
            DateTime now = DateUtil.date();
            //考勤开始时间
            String start = DateUtil.today() + " " + constants.attendanceStartTime;
            //考勤结束时间
            String end = DateUtil.today() + " " + constants.attendanceEndTime;
            //因为要判断当前时间是否为考勤结束时间之后，字符串和日期对象无法比较
            //所以调用DateUtil的parse方法，将获取到的考勤开始、结束时间的字符串转换为日期对象
            DateTime attendanceStart = DateUtil.parse(start);
            DateTime attendanceEnd = DateUtil.parse(end);
            //判断是否为考勤开始时间
            if(now.isBefore(attendanceStart)){
                return "没到上班考勤开始时间";
            }
            //判断是否为考勤结束时间
            else if(now.isAfter(attendanceEnd)){
                return "超过了上班考勤结束时间";
            }else {
                //封装查询到的参数
                HashMap map=new HashMap();
                map.put("userId",userId);
                map.put("date",date);
                map.put("start",start);
                map.put("end",end);
                //如果有考勤记录则返回不用重复考勤，否则反之
                boolean bool=checkinDao.haveCheckin(map)!=null?true:false;
                return bool?"今日已经考勤，不用重复考勤" : "可以考勤";
            }
        }
    }

    /**
     * 签到业务
     * 人脸识别
     * 疫情风险识别
     * 发送告警邮件
     * @param param
     */
    @Override
    public void checkin(HashMap param) {
        Date d1=DateUtil.date();
        //保存上班时间
        Date d2=DateUtil.parse(DateUtil.today()+" "+constants.attendanceTime);
        //保存上班考勤的结束时间
        Date d3=DateUtil.parse(DateUtil.today()+" "+constants.attendanceEndTime);
        int status=1;
        if(d1.compareTo(d2)<=0){
            status=1;//早于上班时间正常签到
        }
        else if(d1.compareTo(d2)>0&&d1.compareTo(d3)<0){
            status=2;//过于上班时间但没过考勤结束时间 迟到
        }
        else{

           throw new EmosException("超出考勤时间段，无法考勤");
        }
        int userId= (Integer) param.get("userId");
        String faceModel=faceModelDao.searchFaceModel(userId);//查找人脸模型数据
        if(faceModel==null){
            throw new EmosException("不存在人脸模型");
        }
        else{
            String path=(String)param.get("path");
            HttpRequest request= HttpUtil.createPost(checkinUrl);//创建Http请求
            request.form("photo", FileUtil.file(path),"targetModel",faceModel);
            request.form("code",code);
            HttpResponse response=request.execute();//给人脸识别程序发送http请求返回响应
            if(response.getStatus()!=200){//判断返回响应的状态码
                log.error("人脸识别服务异常");
                throw new EmosException("人脸识别服务异常");
            }
            String body=response.body();//接收返回响应体字符串
            if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){
                throw new EmosException(body);
            }
            else if("False".equals(body)){//返回False:签到不是人工本人
                throw new EmosException("签到无效，非本人签到");
            }
            else if("True".equals(body)){//返回True:人脸识别成功
                //查询疫情风险等级
                int risk=1;//1 低风险 2 中风险 3高风险
                String city= (String) param.get("city");//城市
                String district= (String) param.get("district");//区县

                String address= (String) param.get("address");
                String country= (String) param.get("country");
                String province= (String) param.get("province");

                if(!StrUtil.isBlank(city)&&!StrUtil.isBlank(district)){
                    String code=cityDao.searchCode(city);//根据城市的名字查询结果
                    try{
                        String url = "http://m." + code + ".bendibao.com/news/yqdengji/?qu=" + district;
                        Document document= Jsoup.connect(url).get();
                        Elements elements=document.getElementsByClass("list");
                        /*
                            查找本地宝对应的css控件,取出控件的内容
                         */
                        if(elements.size()>0){
                            Element element=elements.get(0);
                            String result=element.select("div:last-child").text();
                            /*
                            取出css中最后的div标签内容
                             */
                            if(result.contains("高风")){
                                risk=3;//高风险
                                //发送告警邮件//////
                                HashMap<String,String> map=userDao.searchNameAndDept(userId);
                                String name = map.get("name");
                                String deptName = map.get("dept_name");
                                deptName = deptName != null ? deptName : "";
                                SimpleMailMessage message=new SimpleMailMessage();
                                /*
                                    封装邮件的发送信息
                                 */
                                message.setTo(hrEmail);
                                message.setSubject("员工" + name + "身处高风险疫情地区警告");
                                message.setText(deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，属于新冠疫情高风险地区，请及时与该员工联系，核实情况！");
                                System.out.println("发送了告警邮件:"+deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，属于新冠疫情高风险地区，请及时与该员工联系，核实情况！");
                                emailTask.sendAsync(message);//发送告警邮件
                            }
                            else if(result.contains("中风")){
                                //中风险告警
                                risk=2;
                                HashMap<String,String> map=userDao.searchNameAndDept(userId);
                                String name = map.get("name");
                                String deptName = map.get("dept_name");
                                deptName = deptName != null ? deptName : "";
                                SimpleMailMessage message=new SimpleMailMessage();
                                message.setTo(hrEmail);
                                message.setSubject("员工" + name + "身处中风险疫情地区警告");
                                message.setText(deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，属于新冠疫情高风险地区，请及时与该员工联系，核实情况！");
                                System.out.println("发送了告警邮件:"+deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，属于新冠疫情中风险地区，请及时与该员工联系，核实情况！");
                                emailTask.sendAsync(message);
                            }else {
                                //登录提醒
                                risk=1;
                                HashMap<String,String> map=userDao.searchNameAndDept(userId);
                                String name = map.get("name");
                                String deptName = map.get("dept_name");
                                deptName = deptName != null ? deptName : "";
                                SimpleMailMessage message=new SimpleMailMessage();
                                message.setTo(hrEmail);
                                message.setSubject("员工" + name + "签到提醒");
                                message.setText(deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，进行登录签到!");
                                System.out.println("发送了签到邮件:"+deptName + "员工" + name + "，" + DateUtil.format(new Date(), "yyyy年MM月dd日") + "处于" + address + "，进行登录签到!");
                                emailTask.sendAsync(message);
                            }
                        }
                    }catch (Exception e){
                        log.error("执行异常",e);
                        throw new EmosException("获取风险等级失败");
                    }
                }
                //保存签到记录
                TbCheckin entity=new TbCheckin();
                entity.setUserId(userId);
                entity.setAddress(address);
                entity.setCountry(country);
                entity.setProvince(province);
                entity.setCity(city);
                entity.setDistrict(district);
                entity.setStatus((byte) status);
                entity.setRisk(risk);
                entity.setDate(DateUtil.today());
                entity.setCreateTime(d1);
                checkinDao.insert(entity);
            }
        }
    }

    /**
     * 创建新员工人脸模型
     * @param userId
     * @param path
     */
    @Override
    public void createFaceModel(int userId, String path) {
        HttpRequest request=HttpUtil.createPost(createFaceModelUrl);
        request.form("photo",FileUtil.file(path));
        request.form("code",code);
        HttpResponse response=request.execute();//上传人脸模型数据到Python程序
        String body=response.body();
        if("无法识别出人脸".equals(body)||"照片中存在多张人脸".equals(body)){
            throw new EmosException(body);
        }
        else{//向数据表中保存人脸模型的数据
            TbFaceModel entity=new TbFaceModel();
            entity.setUserId(userId);
            entity.setFaceModel(body);
            faceModelDao.insert(entity);
        }
    }
    /**
     * 查询用户基本信息以及查询用户的基本签到情况
     * @param userId
     * @return HashMap
     */
    @Override
    public HashMap searchTodayCheckin(int userId) {
        HashMap map=checkinDao.searchTodayCheckin(userId);
        return map;
    }

    /**
     * 查询用户总签到天数
     * @param userId
     * @return long
     */
    @Override
    public long searchCheckinDays(int userId) {
        long days=checkinDao.searchCheckinDays(userId);
        return days;
    }

    /**
     * 查询用户在一周内的考勤情况
     * @param param
     * @return ArrayList
     */
    @Override
    public ArrayList<HashMap> searchWeekCheckin(HashMap param) {
        /*
        查询用户的在本周的考勤情况,包括特殊的工作日和节假日
         */
        ArrayList<HashMap> checkinList=checkinDao.searchWeekCheckin(param);
        ArrayList holidaysList=holidaysDao.searchHolidaysInRange(param);
        ArrayList workdayList=workdayDao.searchWorkdayInRange(param);
        /*
        生成本周七天的日期对象
         */
        DateTime startDate=DateUtil.parseDate(param.get("startDate").toString());//起始日期
        DateTime endDate=DateUtil.parseDate(param.get("endDate").toString());//结束日期
        DateRange range=DateUtil.range(startDate,endDate, DateField.DAY_OF_MONTH);//本周七天对象
        /*
        看下得到的日期对象的每一天是工作日还是节假日,如果是工作日,就要查看一下每一天的考勤情况
         */
        ArrayList<HashMap> list=new ArrayList<>();
        //取出本周某一天的信息
        range.forEach(one->{
            String date=one.toString("yyyy-MM-dd");
            String type="工作日";
            if(one.isWeekend()){
                type="节假日";
            }
            if(holidaysList!=null&&holidaysList.contains(date)){
                type="节假日";
            }
            else if(workdayList!=null&&workdayList.contains(date)){//判断是否是工作日
                type="工作日";
            }
            String status="";//如果周五没有到的话默认是空字符串
            //如果是工作日,查看一下考勤的结果
            if(type.equals("工作日")&&DateUtil.compare(one,DateUtil.date())<=0){
                /*
                判断这一天是不是工作日与这一天是否发生 如果没发生status 等于空串 one代表本周的某一天
                 */
                status="缺勤";
                boolean flag=false;//判断是否查到了考勤记录
                for (HashMap<String,String> map:checkinList){
                    //查询是否有这一天的考勤结果
                    if(map.containsValue(date)){
                        status=map.get("status");
                        flag=true;
                        break;
                    }
                }
                /*
                特殊情况:
                    如果当前的考勤没有结束,也没有查询到当天的考勤记录 则应设定status 为空串
                 */
                DateTime endTime=DateUtil.parse(DateUtil.today()+" "+constants.attendanceEndTime);
                String today=DateUtil.today();
                if(date.equals(today)&&DateUtil.date().isBefore(endTime)&&flag==false){//flag 如果没查到考勤记录并且当天考勤没有结束status为空串
                    status="";
                }
            }
            HashMap map=new HashMap();
            map.put("date",date);
            map.put("status",status);
            map.put("type",type);
            map.put("day",one.dayOfWeekEnum().toChinese("周"));
            list.add(map);
        });
        return list;
    }

    /**
     * 查询月考勤
     * @param param
     * @return ArrayList
     */
    @Override
    public ArrayList<HashMap> searchMonthCheckin(HashMap param) {
        return this.searchWeekCheckin(param);
    }
}
